﻿using System;

namespace ExcelTool{
    //JSON处理流程
    using libxl;
    using System.Xml;
    using System.Reflection;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;

    public partial class Converter{

        int json_indent = 0;
        struct FieldEntryIns {
            public XmlFieldEntry cfg;
            public bool if_last;
            public bool if_last_server;

            public string refer_key; //引用本结构Entry的Key，用于拼接表头

            public string sub_sheet_key;

            public string GetTableHead() {
                if(refer_key == string.Empty) {
                    return cfg.key;
                }

                return string.Format("{0}_{1}", refer_key, cfg.key);
            }

            public string GetSubSheetName() {
                if (sub_sheet_key == string.Empty)
                {
                    return cfg.key;
                }

                return string.Format("{0}.{1}", sub_sheet_key, cfg.key);
            }
        }

    #region 根据entry中的type属性，处理数据

        delegate string BaseTypeRead(Sheet sheet, int row, int column);
        bool _enumTypeDataTransfer(Sheet sheet, int row, int column, XmlFieldEntry fieldCfg, ref string data_mod){
            XmlBaseSet set = null;
            if(!getReferenceConfig(fieldCfg, out set)) return false;
            XmlEnumSet eSet = set as XmlEnumSet;
            string excel_data = readStr(sheet, row, column);
            int value = eSet.GetEnumValue(excel_data);
            if(XmlEnumSet.INVALID_VALUE == value) {
                Reporter.PushError("填写了未定义的枚举值 : {0} at {1}", excel_data, Reporter.MakeExcelCellInfo(sheet, row, column));
                return false;
            }
            data_mod = value.ToString();
            return true;
        }

        /// <summary>
        /// 解析基础数据类型
        /// </summary>
        /// <param name="result"></param>
        /// <param name="fieldCfg"></param>
        /// <param name="sheet"></param>
        /// <param name="row"></param>
        /// <param name="output_format"></param>
        /// <param name="readFunc"></param>
        /// <returns></returns>
        bool _writeBaseTypeData(StringBuilderSelector result, FieldEntryIns feIns, 
                                Sheet sheet, int row, string[] output_format, 
                                BaseTypeRead readFunc) {
            XmlFieldEntry fieldCfg = feIns.cfg; 
            if(fieldCfg.count_num== 1) {
                int column;
                if(!_getColIndex(sheet, feIns.GetTableHead(), out column)) return false;
                string data = readFunc(sheet, row, column);
                if (feIns.cfg.ParentSet.type == XML_DATA_TYPE_NAME.CLASS 
                    && feIns.cfg.Primary == true)
                {
                    //校验主键的唯一性，并缓存
                    XmlClassSet cs = feIns.cfg.ParentSet as XmlClassSet;
                    string debug_str = string.Empty;
                    if (!cs.CheckPrimaryUniqueness(data, out debug_str))
                    {
                        Reporter.PushError("主键 : 【{0}】重复于 \n{1}\n{2}", data, debug_str, Reporter.MakeExcelCellInfo(sheet, row, column));
                        return false;
                    }

                    cs.CachePrimaryValue(data, Reporter.MakeExcelCellInfo(sheet, row, column));
                }
                if(fieldCfg.type == XML_DATA_TYPE_NAME.ENUM &&
                    !_enumTypeDataTransfer(sheet, row, column, fieldCfg, ref data)) {
                    return false;
                }
                FormatHelper.JsonContent(result, string.Format(output_format[0], fieldCfg.Primary == true ? FILE_FORMAT.PRIMARY_NAME : fieldCfg.name, data), json_indent, feIns.if_last, feIns.if_last_server);
            }
            else {
                FormatHelper.JsonContent(result, string.Format(output_format[1], fieldCfg.name), json_indent, true, true);
                FormatHelper.JsonLeftBracket(result, ref json_indent);
                for(int i = 0; i < fieldCfg.count_num; i++) {
                    int column;
                    if(!_getColIndex(sheet, string.Format("{0}{1}", feIns.GetTableHead(), (i + 1)), out column)) return false;
                    string data = readFunc(sheet, row, column);
                    if(fieldCfg.type == XML_DATA_TYPE_NAME.ENUM &&
                        !_enumTypeDataTransfer(sheet, row, column, fieldCfg, ref data)) {
                        return false;
                    }
                    bool last = i == (fieldCfg.count_num - 1);
                    FormatHelper.JsonContent(result, string.Format(output_format[2], data), json_indent, last, last);
                }
                FormatHelper.JsonRightBracket(result, ref json_indent, feIns.if_last, feIns.if_last_server);
            }
            return true;
        }
        bool intProcessFunc(StringBuilderSelector result, FieldEntryIns feIns, Sheet sheet, int row) {
            bool ret = _writeBaseTypeData(result, feIns, sheet, row, 
                                            new string[] { "\"{0}\":{1}", "\"{0}\":", "{0}" }, 
                                            (s, r, c) =>  { return readInt(s, r, c).ToString(); });
            return ret;
        }
        bool doubleProcessFunc(StringBuilderSelector result, FieldEntryIns feIns, Sheet sheet, int row) {
            bool ret = _writeBaseTypeData(result, feIns, sheet, row, 
                                            new string[] { "\"{0}\":{1}", "\"{0}\":", "{0}" }, 
                                            (s, r, c) =>  { return readDouble(s, r, c).ToString(); });
            return ret;
        }
        bool stringProcessFunc(StringBuilderSelector result, FieldEntryIns feIns, Sheet sheet, int row) {
            bool ret = _writeBaseTypeData(result, feIns, sheet, row, 
                                            new string[] { "\"{0}\":\"{1}\"", "\"{0}\":", "\"{0}\"" }, 
                                            (s, r, c) => { return readStr(s, r, c); });
            return ret;
        }
        bool enumProcessFunc(StringBuilderSelector result, FieldEntryIns feIns, Sheet sheet, int row) {
            bool ret = _writeBaseTypeData(result, feIns, sheet, row, 
                                            new string[] { "\"{0}\":{1}", "\"{0}\":", "{0}" }, 
                                            (s, r, c) =>  { return readStr(s, r, c); });
            return ret;
        }

        bool _processSingleSubClass(StringBuilderSelector result, XmlSubClassSet sc, 
                                        FieldEntryIns feIns, Sheet sheet, int row, 
                                        int count_idx, bool b_client_last, bool b_server_last) {
            FormatHelper.JsonLeftBrace(result, ref json_indent);
            for(int i = 0; i < sc.fieldCfg.Count; i++) {
                XmlFieldEntry entryCfg = sc.fieldCfg[i];
                FieldEntryIns entryParam;
                entryParam.cfg = entryCfg;
                entryParam.if_last = (i == (sc.client_count - 1));
                entryParam.if_last_server = (i == (sc.server_count - 1));
                entryParam.refer_key = count_idx <= 0 ? feIns.GetTableHead() : string.Format("{0}{1}", feIns.GetTableHead(), count_idx);
                entryParam.sub_sheet_key = feIns.GetSubSheetName();
                if(!_rowParser(result, entryParam, sheet, row)) return false;
            }
            FormatHelper.JsonRightBrace(result, ref json_indent, b_client_last, b_server_last);
            return true;
        }
        bool subclassProcessFunc(StringBuilderSelector result, FieldEntryIns feIns, Sheet sheet, int row) {
            XmlBaseSet set = null;
            if(!getReferenceConfig(feIns.cfg, out set)) return false;
            XmlSubClassSet sc = set as XmlSubClassSet;

            FormatHelper.JsonContent(result, string.Format("\"{0}\":", feIns.cfg.name), json_indent, true, true);
            if(feIns.cfg.count_num== 1) {
                //FormatHelper.JsonLeftBrace(result, ref json_indent);
                //for(int i = 0; i < sc.fieldCfg.Count; i++) {
                //    XmlFieldEntry entryCfg = sc.fieldCfg[i];
                //    FieldEntryIns entryParam;
                //    entryParam.cfg = entryCfg;
                //    entryParam.if_last = (i == (sc.client_count - 1));
                //    entryParam.if_last_server = (i == (sc.server_count- 1));
                //    entryParam.refer_key = feIns.GetTableHead();
                //    entryParam.sub_sheet_key = feIns.GetSubSheetName();
                //    if(!_rowParser(result, entryParam, sheet, row)) return false;
                //}
                //FormatHelper.JsonRightBrace(result, ref json_indent, feIns.if_last, feIns.if_last_server);
                if(!_processSingleSubClass(result, sc, feIns, sheet, row, -1, feIns.if_last, feIns.if_last_server)) return false;
            }
            else {
                FormatHelper.JsonLeftBracket(result, ref json_indent);
                for(int cfg_count = 0; cfg_count < feIns.cfg.count_num; cfg_count++) {
                    //FormatHelper.JsonLeftBrace(result, ref json_indent);
                    //for(int i = 0; i < sc.fieldCfg.Count; i++) {
                    //    XmlFieldEntry entryCfg = sc.fieldCfg[i];
                    //    FieldEntryIns entryParam;
                    //    entryParam.cfg = entryCfg;
                    //    entryParam.if_last = (i == (sc.client_count - 1));
                    //    entryParam.if_last_server = (i == (sc.server_count - 1));
                    //    entryParam.refer_key = string.Format("{0}{1}", feIns.GetTableHead(), cfg_count + 1);
                    //    entryParam.sub_sheet_key = feIns.GetSubSheetName();
                    //    if(!_rowParser(result, entryParam, sheet, row)) return false;
                    //}
                    bool last = cfg_count == (feIns.cfg.count_num - 1);
                    //FormatHelper.JsonRightBrace(result, ref json_indent, last, last);
                    if(!_processSingleSubClass(result, sc, feIns, sheet, row, cfg_count + 1, last, last)) return false;
                }
                FormatHelper.JsonRightBracket(result, ref json_indent, feIns.if_last, feIns.if_last_server);
            }

            return true;
        }

        bool _parseSubSheet(StringBuilderSelector result, FieldEntryIns feIns, 
                            Sheet sheet, int row, int column ) {
            try
            {
                XmlBaseSet set = null;
                if(!getReferenceConfig(feIns.cfg, out set)) return false;
                XmlGroupSet groupset = set as XmlGroupSet;
                int target_group = readInt(sheet, row, column);
                if (target_group > 0)
                {
                    FormatHelper.JsonContent(result, "\"group\":", json_indent, true, true);
                    FormatHelper.JsonLeftBracket(result, ref json_indent);
                    Sheet subSheet;
                    if (!GetSheetByName(sheet.book.FilePath, feIns.GetSubSheetName(), out subSheet)) return false;
                    List<int> rowList = GetRowListById(subSheet, target_group);

                    if (rowList == null)
                    {
                        Reporter.PushError("表：【{3}】的Sheet:【{0}】中不存在ID为:【{1}】的数据组索引；该数据组索引填写于[{2}]", 
                            subSheet.name, target_group, Reporter.MakeExcelCellInfo(sheet, row, column), subSheet.book.FilePath);
                        return false;
                    }
                    for (int i = 0; i < rowList.Count; i++)
                    {
                        int rowIdx = rowList[i];
                        FormatHelper.JsonLeftBrace(result, ref json_indent);
                        if(!traverseClassSet(result, groupset, subSheet, rowIdx)) return false; 

                        bool last = i == (rowList.Count - 1);
                        FormatHelper.JsonRightBrace(result, ref json_indent, last, last);
                    }


                    FormatHelper.JsonRightBracket(result, ref json_indent, true, true);
                }

            }
            catch (System.Exception e)
            {
                Reporter.PushError("处理Excel: 【{0}】SubSheet: 【{1}】异常！\n{2}", sheet.book.FilePath, sheet.name, e);
                return false;
            }
            return true;
        }

        bool groupProcessFunc(StringBuilderSelector result, FieldEntryIns feIns, Sheet sheet, int row) {
            FormatHelper.JsonContent(result, string.Format("\"{0}\":", feIns.cfg.name), json_indent, true, true);
            //FormatHelper.JsonLeftBracket(result, ref json_indent);
            if(feIns.cfg.count_num == 1) {
                int column = -1;
                if(!_getColIndex(sheet, feIns.GetTableHead(), out column)) return false;

                FormatHelper.JsonLeftBrace(result, ref json_indent);
                if(!_parseSubSheet(result, feIns, sheet, row, column)) return false;
                FormatHelper.JsonRightBrace(result, ref json_indent, feIns.if_last, feIns.if_last_server);
            }
            else {
                FormatHelper.JsonLeftBracket(result, ref json_indent);
                for (int cfg_count = 0; cfg_count < feIns.cfg.count_num; cfg_count++)
                {
                    int column = -1;
                    if(!_getColIndex(sheet, string.Format("{0}{1}", feIns.GetTableHead(), cfg_count + 1), out column)) return false;

                    FormatHelper.JsonLeftBrace(result, ref json_indent);
                    if(!_parseSubSheet(result, feIns, sheet, row, column)) return false;
                    bool last = cfg_count == (feIns.cfg.count_num - 1);
                    FormatHelper.JsonRightBrace(result, ref json_indent, last, last);
                }
                FormatHelper.JsonRightBracket(result, ref json_indent, feIns.if_last, feIns.if_last_server);
            }
            //FormatHelper.JsonRightBracket(result, ref json_indent, feIns.if_last);

            return true;
        }
    #endregion
        bool _rowParser(StringBuilderSelector result, FieldEntryIns param, Sheet sheet, int row) {
            XmlFieldEntry fieldCfg = param.cfg;
            Reporter.RecordConverterInfo(fieldCfg);
            if(!XML_DATA_TYPE_NAME.CheckTypeValid(fieldCfg.type)) {
                Reporter.PushError("Type:{0} 非有效类型!", fieldCfg.type);
                return false;
            }
            MethodInfo methodInfo = typeof(Converter).GetMethod(string.Format("{0}ProcessFunc", fieldCfg.type), BindingFlags.Instance | BindingFlags.NonPublic);
            try {
                bool ret = (bool)methodInfo.Invoke(this, new object[] { result, param, sheet, row});
                if(!ret) return false;
            }
            catch(System.Exception e) {
                Reporter.PushError("解析Excel单元失败！\n{0}", e);
                return false;
            }

            return true;
        }

        #region util
        bool getReferenceConfig(XmlFieldEntry fieldCfg, out XmlBaseSet setConfig) {
            string ref_ename = fieldCfg.type_param;
            setConfig = null;
            try {
                if(fieldCfg.ParentSet.ParentLib.ReferenceSets.ContainsKey(ref_ename)) { //先在同一个XmlMetalib中寻找
                    fieldCfg.ParentSet.ParentLib.ReferenceSets.TryGetValue(ref_ename, out setConfig);
                }
                else if(QueryCommonDefine(ref_ename)){
                    setConfig = GetReferenceOfCommonDefine(ref_ename);
                }
                else {
                    Reporter.PushError("Type_param : {0}没有找到定义!", fieldCfg.type_param);
                    return false;
                }

                if(setConfig == null) {
                    Reporter.PushError("获取Set配置失败！");
                    return false;
                }
            }
            catch(System.Exception e) {
                Reporter.PushError("Json转换流程失败 : {0}", e);
                return false;
            }
            return true;
        }
        bool _getColIndex(Sheet sheet, string name, out int column) {
            for(int i = 0; i < sheet.lastCol(); i++) {
                string headname = sheet.readStr(0, i);
                if( headname == name) {
                    column = i;
                    return true;
                }
            }
            Reporter.PushError("excel:{0}, sheet:{1}中未找到列:{2}", sheet.book.FilePath, sheet.name, name);
            column = -1;
            return false;
        }
        #endregion

        #region 生成json文件列表
        List<string> m_clientJsonList = new List<string>();
        List<string> m_serverJsonList = new List<string>();
        int path_info_indent = 0;
        bool genJsonFileList() {
            StringBuilderSelector sb_selector = new StringBuilderSelector();
            sb_selector.mode = JSON_MODE.COMMON;

            FormatHelper.JsonLeftBrace(sb_selector, ref path_info_indent);
            FormatHelper.JsonContent(sb_selector, "\"list\":", path_info_indent, true, true);
            FormatHelper.JsonLeftBracket(sb_selector, ref path_info_indent);
            sb_selector.mode = JSON_MODE.CLIENT;
            for (int i = 0; i < m_clientJsonList.Count; i++)
            {
                bool last = (i == (m_clientJsonList.Count - 1));
                FormatHelper.JsonContent(sb_selector, string.Format("\"{0}\"", m_clientJsonList[i]), path_info_indent, last, last);
            }
            sb_selector.mode = JSON_MODE.SERVER;
            for (int i = 0; i < m_serverJsonList.Count; i++)
            {
                bool last = (i == (m_serverJsonList.Count - 1));
                FormatHelper.JsonContent(sb_selector, string.Format("\"{0}\"", m_serverJsonList[i]), path_info_indent, last, last);
            }
            sb_selector.mode = JSON_MODE.COMMON;
            FormatHelper.JsonRightBracket(sb_selector, ref path_info_indent, true, true);
            FormatHelper.JsonRightBrace(sb_selector, ref path_info_indent, true, true);

            WriteText(Path.Combine(FILE_PATH.CLIENT_JSON_WORKING_DIR, string.Format("{0}{1}", FILE_PATH.JSON_FILE_LIST_NAME, FILE_EXT.JSON)), sb_selector.client_sb.ToString());
            WriteText(Path.Combine(FILE_PATH.SERVER_JSON_WORKING_DIR, string.Format("{0}{1}", FILE_PATH.JSON_FILE_LIST_NAME, FILE_EXT.JSON)), sb_selector.server_sb.ToString());

            m_clientJsonList.Clear();
            m_serverJsonList.Clear();
            return true;
        }
        #endregion


        bool jsonConverter() {
            foreach(var lib_it in m_allLib) { //遍历Xml文件
                XmlMetalib lib = lib_it.Value;
                for(int cs_it = 0; cs_it < lib.class_config.Count; cs_it++) {//遍历每个XmlMetalib(XML文件)中的Class（为一份json数据）
                    XmlClassSet csit = lib.class_config[cs_it];

                    StringBuilderSelector json_sb_selector = new StringBuilderSelector();

                    json_sb_selector.mode = csit.mode;
                    FormatHelper.JsonLeftBrace(json_sb_selector, ref json_indent);
                    FormatHelper.JsonContent(json_sb_selector, string.Format("\"{0}\":", FILE_FORMAT.JSON_DATA_KEY), json_indent, true, true);
                    FormatHelper.JsonLeftBracket(json_sb_selector, ref json_indent);

                    int total_count = 0;  //统计级联表格中所有的有效数据行数
                    for(int xl_it = 0; xl_it < csit.DataSourceList.Count; xl_it++) { //遍历级联Excel表
                        string pathName = csit.DataSourceList[xl_it].tablename;
                        Book xlBook = m_excel[pathName];
                        Sheet sheet = xlBook.getSheet(EXCEL_FILE_RULE.MAIN_DATA_SHEET_INDEX);  //默认第一张Sheet为主表
                        int row_count = sheet.lastRow();
                        for(int i = EXCEL_FILE_RULE.TABLEHEAD_LINE_COUNT; i < row_count; i++) { //遍历Excel中每一行数据
                            total_count++;
                            FormatHelper.JsonLeftBrace(json_sb_selector, ref json_indent);

                            if(!traverseClassSet(json_sb_selector, csit, sheet, i)) return false;
                            
                            json_sb_selector.mode = csit.mode;
                            bool last = total_count == csit.TotalDataCount;
                            FormatHelper.JsonRightBrace(json_sb_selector, ref json_indent, last, last);
                        }
                    }

                    FormatHelper.JsonRightBracket(json_sb_selector, ref json_indent, true, true);
                    FormatHelper.JsonRightBrace(json_sb_selector, ref json_indent, true, true);

                    //json filename = libname + classname
                    string json_filename = string.Format("tb_{0}_{1}{2}", lib.lib_name.ToString().ToLower(), csit.name.ToLower(), FILE_EXT.JSON);

                    string client_content = json_sb_selector.client_sb.ToString();
                    if(!string.IsNullOrEmpty(client_content)) {
                        m_clientJsonList.Add(json_filename);
                        string jsonClientPath = Path.Combine(FILE_PATH.CLIENT_JSON_WORKING_DIR, json_filename);
                        Console.WriteLine("输出client【json】 : {0}", jsonClientPath);
                        WriteText(jsonClientPath, json_sb_selector.client_sb.ToString());
                    }

                    string server_content = json_sb_selector.server_sb.ToString();
                    if(!string.IsNullOrEmpty(server_content)) {
                        m_serverJsonList.Add(json_filename);
                        string jsonServerPath = Path.Combine(FILE_PATH.SERVER_JSON_WORKING_DIR, json_filename);
                        Console.WriteLine("输出server【json】 : {0}", jsonServerPath);
                        WriteText(jsonServerPath, json_sb_selector.server_sb.ToString());
                    }
                }
            }

            genJsonFileList();
            return true;
        }
        bool doEachClassEntry(XmlFieldEntry item, ref int server_it, ref int client_it,
                                    StringBuilderSelector json_sb_selector, XmlBaseRefer csit, Sheet sheet, int excel_row_idx) {
            FieldEntryIns param;
            param.cfg = item;
            param.if_last = (client_it == (csit.client_count - 1));
            param.if_last_server = (server_it == (csit.server_count - 1));
            param.refer_key = string.Empty;
            param.sub_sheet_key = string.Empty;

            if(!_rowParser(json_sb_selector, param, sheet, excel_row_idx)) return false;

            if(item.mode == JSON_MODE.COMMON || item.mode == JSON_MODE.CLIENT) {
                client_it++;
            }
            if(item.mode == JSON_MODE.COMMON || item.mode == JSON_MODE.SERVER) {
                server_it++;
            }
            return true;
        }

        bool traverseClassSet(StringBuilderSelector json_sb_selector, XmlBaseRefer csit, Sheet sheet, int excel_row_idx) {
            int server_it = 0;
            int client_it = 0;

            bool override_mode = false;
            if(csit as XmlClassSet != null) {
                //只有从class进入本函数，才进行mode的赋值
                //由嵌套结构进入本函数的，始终保持最外层（class层）的mode设置
                override_mode = true;
            }
            foreach(XmlFieldEntry item in csit.fieldCfg) {
                if(override_mode) json_sb_selector.mode = item.mode;
                if(!doEachClassEntry(item, ref server_it, ref client_it, json_sb_selector, csit, sheet, excel_row_idx)) return false;
            }
            return true;
        }
    }
}
